﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  ScheduleTest.cs
//    
// Abstract:
//
//  Unit tests for the Schedule class.
//  
//--

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using TestHelpers = Microsoft.EnhancedReminders.UnitTests.Helpers;

namespace Microsoft.EnhancedReminders.Scheduler.UnitTests
{
    /// <summary>
    /// This is a test class for Schedule and is intended
    /// to contain all Schedule Unit Tests
    /// </summary>
    [TestClass()]
    public class ScheduleTest
    {
        // Indicates if the Updated event has been raised
        private bool _updateRaised;

        // The Schedule instance being tested
        private Schedule_Accessor _target;

        /// <summary>
        /// Create a new Schedule instance for each test.
        /// </summary>
        [TestInitialize()]
        public void MyTestInitialize()
        {
            _target = new Schedule_Accessor((ExchangeUsers_Accessor) null);
        }

        /// <summary>
        /// Tests that an empty queue returns the correct values from NextTime and RemoveNext.
        /// </summary>
        [TestMethod()]
        public void ScheduleEmptyTest()
        {
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests adding a new reminder to an empty schedule.
        /// </summary>
        /// <remarks>
        /// The test adds a reminder to the schedule and then verifies that the same reminder is returned when RemoveNext 
        /// is called. The test also verifies that the schedule is empty after RemoveNext is called.
        /// </remarks>
        [TestMethod()]
        public void SimpleAddItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            NotificationItem expectedItem = TestHelpers.CreateNotificationItem("1", expectedTime);
            VerifyAddItem(expectedItem, expectedTime, true);
            VerifyRemoveNext(expectedItem);
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests adding a pair of new reminders to an empty schedule.
        /// </summary>
        /// <remarks>
        /// The test adds a pair of reminders (as if from the same calendar item) to the schedule and then verifies that the 
        /// correct reminder is returned when RemoveNext is called. The test also verifies that the schedule is empty after 
        /// RemoveNext is called.
        /// </remarks>
        [TestMethod()]
        public void SimpleAddMultipleItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            NotificationItem[] expectedItems = new NotificationItem[2];
            expectedItems[0] = TestHelpers.CreateNotificationItem("1", expectedTime + TimeSpan.FromMinutes(1));
            expectedItems[1] = TestHelpers.CreateNotificationItem("1", expectedTime);
            VerifyAddItems(expectedItems, expectedTime, true);
            VerifyRemoveNext(expectedItems[1]);
            VerifyRemoveNext(expectedItems[0]); 
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests adding multiple new reminders to a schedule.
        /// </summary>
        /// <remarks>
        /// The test adds three reminder to the schedule and verifies that RemoveNext returns the correct reminder each time 
        /// it is called. The test also verifies that the schedule is empty after RemoveNext is called.
        /// </remarks>
        [TestMethod()]
        public void ComplexAddItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            NotificationItem expectedItem1 = TestHelpers.CreateNotificationItem("1", expectedTime);
            NotificationItem expectedItem2 = TestHelpers.CreateNotificationItem("2", expectedTime + TimeSpan.FromMinutes(1));
            NotificationItem expectedItem3 = TestHelpers.CreateNotificationItem("3", expectedTime + TimeSpan.FromMinutes(2));
            VerifyAddItem(expectedItem2, expectedTime + TimeSpan.FromMinutes(1), true);
            VerifyAddItem(expectedItem3, expectedTime + TimeSpan.FromMinutes(1), false);
            VerifyAddItem(expectedItem1, expectedTime, true);
            VerifyRemoveNext(expectedItem1);
            VerifyRemoveNext(expectedItem2);
            VerifyRemoveNext(expectedItem3);
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests modifying an existing reminder in the schedule (without changing the time of the reminder).
        /// </summary>
        /// <remarks>
        /// The test adds a reminder to the schedule and then adds a modified version of the same reminder. The test verifies 
        /// that the reminder in the schedule is correctly modified.
        /// </remarks>
        [TestMethod()]
        public void SimpleModifyItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            CalendarNotificationItem expectedItem = TestHelpers.CreateNotificationItem("1", expectedTime);
            VerifyAddItem(expectedItem, expectedTime, true);
            CalendarNotificationItem expectedItemNew = TestHelpers.CreateNotificationItem("1", expectedTime);
            VerifyAddItem(expectedItemNew, expectedTime, true);
            VerifyRemoveNext(expectedItemNew);
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests modifying an existing reminder in the schedule multiple times.
        /// </summary>
        /// <remarks>
        /// The test adds a reminder to the schedule and then adds the same reminder multiple times. The test verifies that 
        /// there is only one reminder in the schedule.
        /// </remarks>
        [TestMethod()]
        public void RepeatedModifyItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            NotificationItem expectedItem = TestHelpers.CreateNotificationItem("1", expectedTime);
            VerifyAddItem(expectedItem, expectedTime, true);
            VerifyAddItem(expectedItem, expectedTime, true);
            VerifyAddItem(expectedItem, expectedTime, true);
            VerifyRemoveNext(expectedItem);
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests modifying an existing reminder in the schedule by changing the time of the reminder.
        /// </summary>
        /// <remarks>
        /// The test adds three reminders to the schedule and then modifies the reminder-due-time for one of them. The test 
        /// verifies that the modified reminder is correctly re-positioned in the schedule based on the new time.
        /// </remarks>
        [TestMethod()]
        public void TimeChangeModifyItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            NotificationItem expectedItem1 = TestHelpers.CreateNotificationItem("1", expectedTime);
            NotificationItem expectedItem2 = TestHelpers.CreateNotificationItem("2", expectedTime + TimeSpan.FromMinutes(1));
            NotificationItem expectedItem3 = TestHelpers.CreateNotificationItem("3", expectedTime + TimeSpan.FromMinutes(2));
            VerifyAddItem(expectedItem1, expectedTime, true);
            VerifyAddItem(expectedItem3, expectedTime, false);
            VerifyAddItem(expectedItem2, expectedTime, false);
            NotificationItem expectedItemNew2 = TestHelpers.CreateNotificationItem("2", expectedTime - TimeSpan.FromMinutes(1));
            VerifyAddItem(expectedItemNew2, expectedTime - TimeSpan.FromMinutes(1), true);
            expectedItemNew2 = TestHelpers.CreateNotificationItem("2", expectedTime + TimeSpan.FromMinutes(1));
            VerifyAddItem(expectedItemNew2, expectedTime, true);
            VerifyRemoveNext(expectedItem1);
            VerifyRemoveNext(expectedItemNew2);
            VerifyRemoveNext(expectedItem3);
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests deleting an existing reminder in the schedule.
        /// </summary>
        /// <remarks>
        /// The test adds a reminder to the schedule and then deletes it. The test verifies that this leaves the 
        /// schedule empty.
        /// </remarks>
        [TestMethod()]
        public void SimpleDeleteItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            NotificationItem expectedItem = TestHelpers.CreateNotificationItem("1", expectedTime);
            VerifyAddItem(expectedItem, expectedTime, true);
            VerifyDeleteItem(expectedItem.Id.Id, DateTime.MaxValue, true);
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests deleting an existing pair of reminders in the schedule.
        /// </summary>
        /// <remarks>
        /// The test adds a pair of reminders to the schedule (as if from the same calendar item) and then deletes them. The
        /// test verifies that this leaves the schedule empty.
        /// </remarks>
        [TestMethod()]
        public void SimpleDeleteMultipleItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            NotificationItem[] expectedItems = new NotificationItem[2];
            expectedItems[0] = TestHelpers.CreateNotificationItem("1", expectedTime + TimeSpan.FromMinutes(1));
            expectedItems[1] = TestHelpers.CreateNotificationItem("1", expectedTime);
            VerifyAddItems(expectedItems, expectedTime, true);
            VerifyDeleteItem(expectedItems[0].Id.Id, DateTime.MaxValue, true);
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Tests deleting multiple existing reminders in the shcedule.
        /// </summary>
        /// <remarks>
        /// The test adds three reminders to the schedule and then deletes them. The test verifies that the state of the 
        /// schedule is correct after each reminder is deleted.
        /// </remarks>
        [TestMethod()]
        public void ComplexDeleteItemTest()
        {
            DateTime expectedTime = DateTime.Now;
            NotificationItem expectedItem1 = TestHelpers.CreateNotificationItem("1", expectedTime);
            NotificationItem expectedItem2 = TestHelpers.CreateNotificationItem("2", expectedTime + TimeSpan.FromMinutes(1));
            NotificationItem expectedItem3 = TestHelpers.CreateNotificationItem("3", expectedTime + TimeSpan.FromMinutes(2));
            VerifyAddItem(expectedItem3, expectedTime + TimeSpan.FromMinutes(2), true);
            VerifyAddItem(expectedItem2, expectedTime + TimeSpan.FromMinutes(1), true);
            VerifyAddItem(expectedItem1, expectedTime, true);
            VerifyDeleteItem(expectedItem2.Id.Id, expectedTime, false);
            VerifyDeleteItem(expectedItem1.Id.Id, expectedTime + TimeSpan.FromMinutes(2), true);
            VerifyDeleteItem(expectedItem3.Id.Id, DateTime.MaxValue, true);
            VerifyRemoveNext(null /* NotificationItem */);
        }

        /// <summary>
        /// Adds the specified reminder to the schedule and verifies that the Updated event is raised correctly and NextTime is as expected.
        /// </summary>
        /// <param name="item">Reminder to be added</param>
        /// <param name="expectedNextTime">Expected NextTime after the reminder is added</param>
        /// <param name="expectUpdate">Indicates if the Updated event is expected to be raised</param>
        private void VerifyAddItem(NotificationItem item, DateTime expectedNextTime, bool expectUpdate)
        {
            _updateRaised = false;
            _target.Updated += Schedule_Updated;
            _target.ExchangeUsers_ItemUpdated(null /* sender */, new ItemUpdatedEventArgs_Accessor(item.Id.Id, new NotificationItem[] { item }));
            _target.Updated -= Schedule_Updated; 
            Assert.AreEqual(expectUpdate, _updateRaised, "Unexpected update event");
            VerifyNextTime(expectedNextTime);
        }

        /// <summary>
        /// Adds the specified reminder to the schedule and verifies that the Updated event is raised correctly and NextTime is as expected.
        /// </summary>
        /// <param name="items">Reminders to be added</param>
        /// <param name="expectedNextTime">Expected NextTime after the reminder is added</param>
        /// <param name="expectUpdate">Indicates if the Updated event is expected to be raised</param>
        private void VerifyAddItems(NotificationItem[] items, DateTime expectedNextTime, bool expectUpdate)
        {
            Assert.IsTrue(items.Length > 0, "items.Length > 0");
            _updateRaised = false;
            _target.Updated += Schedule_Updated;
            _target.ExchangeUsers_ItemUpdated(null /* sender */, new ItemUpdatedEventArgs_Accessor(items[0].Id.Id, items));
            _target.Updated -= Schedule_Updated;
            Assert.AreEqual(expectUpdate, _updateRaised, "Unexpected update event");
            VerifyNextTime(expectedNextTime);
        }

        /// <summary>
        /// Deletes the specified reminder from the schedule and verifies that the Updated event is raised correctly and NextTime is as expected.
        /// </summary>
        /// <param name="id">ID of the reminder to be deleted</param>
        /// <param name="expectedNextTime">Expected NextTime after the reminder is deleted</param>
        /// <param name="expectUpdate">Indicates if the Updated event is expected to be raised</param>
        private void VerifyDeleteItem(string id, DateTime expectedNextTime, bool expectUpdate)
        {
            _updateRaised = false;
            _target.Updated += Schedule_Updated;
            _target.ExchangeUsers_ItemDeleted(null /* sender */, new ItemDeletedEventArgs(id));
            _target.Updated -= Schedule_Updated; 
            Assert.AreEqual(expectUpdate, _updateRaised, "Unexpected update event");
            VerifyNextTime(expectedNextTime);
        }

        /// <summary>
        /// Verifies that NextTime has the expected value.
        /// </summary>
        /// <param name="expected">Expected value of NextTime</param>
        private void VerifyNextTime(DateTime expected)
        {
            DateTime actual = _target.NextTime;
            Assert.AreEqual(expected, actual, "Unexpected NextTime");
        }

        /// <summary>
        /// Removes the next reminder from the schedule and verifies that it has the expected value.
        /// </summary>
        /// <param name="expected">Expected next reminder</param>
        private void VerifyRemoveNext(NotificationItem expected)
        {
            VerifyNextTime(expected != null ? expected.NotificationTime : DateTime.MaxValue);
            NotificationItem actual = _target.RemoveNext();
            Assert.AreEqual(expected, actual, "Unexpected RemoveNext");
        }

        /// <summary>
        /// Invoked when the Schedule.Updated event is raised for the Schedule instance under test.
        /// </summary>
        /// <param name="sender">Not used</param>
        /// <param name="e">Not used</param>
        private void Schedule_Updated(object sender, EventArgs e)
        {
            _updateRaised = true;
        }
    }
}
